package com.wsdk.util;

import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;

import com.wsdk.Wsdk;
import com.wsdk.constants.SPConstants;
import com.wsdk.constants.UrlConstants;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 *
 * @author fionera
 * @date 16-4-25
 */
public class OkHttpUtil {
    protected static final OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
            .sslSocketFactory(createSSLSocketFactory(), createTrustAllManager())
            .hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String s, SSLSession sslSession) {
                    // now all
                    return true;
                }
            })
            .readTimeout(25, TimeUnit.SECONDS)
            .build();
    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    private static final MediaType MEDIA_TYPE_JPEG = MediaType.parse("image/jpeg");
    protected static final Handler handler = new Handler(Looper.getMainLooper());
    private static String session = "";

    public static SSLSocketFactory createSSLSocketFactory() {
        SSLSocketFactory sslSocketFactory = null;
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{createTrustAllManager()}, new SecureRandom());
            sslSocketFactory = sslContext.getSocketFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sslSocketFactory;
    }

    private static X509TrustManager createTrustAllManager() {
        X509TrustManager tm = null;
        try {
            tm = new X509TrustManager() {
                @SuppressLint("TrustAllX509TrustManager")
                @Override
                public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
                    // nothing
                }

                @SuppressLint("TrustAllX509TrustManager")
                @Override
                public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
                    // nothing
                }

                @Override
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return new java.security.cert.X509Certificate[0];
                }
            };
        } catch (Exception e) {
            e.printStackTrace();
        }
        return tm;
    }

    public static void getEnqueue(String url, final NetCallBack responseCallback, Object tag) {
        try {
            url = UrlConstants.ROOTURL + url;
            getEnqueueWithHeader(url, responseCallback, tag, true, false);
        } catch (Exception e) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    responseCallback.onFailed("网络错误");
                }
            });
        }
    }

    public static void getEnqueueWithoutToken(String url, final NetCallBack responseCallback, Object tag) {
        try {
            url = UrlConstants.ROOTURL + url;
            getEnqueueWithHeader(url, responseCallback, tag, false, false);
        } catch (Exception e) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    responseCallback.onFailed("网络错误");
                }
            });
        }
    }

    private static void getEnqueueWithHeader(String url, final NetCallBack responseCallback,
                                             Object tag, boolean needToken,
                                             final boolean getSession) {
        if (isNetworkNotConnected(Wsdk.getInstance())) {
            onNoNetwork(responseCallback);
            return;
        }

        Request.Builder builder = new Request.Builder().url(url);
        if (!TextUtils.isEmpty(session)) {
            builder.addHeader("Cookie", session);
        }
        if (needToken && !TextUtils.isEmpty(SPUtil.getString(SPConstants.EQUIPSN))) {
            builder.addHeader("equipSN", SPUtil.getString(SPConstants.EQUIPSN));
        }
        if (!TextUtils.isEmpty(SPUtil.getString(SPConstants.TOKEN))) {
            builder.addHeader("Authorization", SPUtil.getString(SPConstants.TOKEN));
        }
        builder.addHeader("ClientType", "mobileApp");
        Request request = builder.tag(tag).build();

        StringBuilder headerString = new StringBuilder("\n");
        Headers headers = request.headers();
        for (int i = 0; i < headers.size(); i++) {
            if (TextUtils.equals("Authorization", headers.name(i))) {
                continue;
            }
            headerString.append(headers.name(i)).append(":").append(headers.value(i)).append("\n");
        }

        LogUtil.d("GET_URL - " + url + "\nHEADER - " + headerString.toString());
        final String endUrl = url;
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                LogUtil.d("GET_ERROR - " + endUrl + "\n" + e.getMessage());
                if (TextUtils.equals(e.getMessage(), "Canceled") || TextUtils.equals(e.getMessage(),
                        "Socket closed")) {
                    return;
                }
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        responseCallback.onFailed("网络超时");
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String result = response.body().string();
                if (getSession) {
                    session = response.header("Set-Cookie", "");
                    LogUtil.d("获取到Cookie：" + session);
                }
                LogUtil.d("GET_RETURN - " + endUrl + "\n" + result);
                String returnCode = "404";
                String returnMsg = "网络超时";
                try {
                    returnCode = new JSONObject(result).getString("returnCode");
                    returnMsg = new JSONObject(result).getString("returnMsg");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                final String finalMsg = returnMsg;
                if ("200".equals(returnCode)) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                String finalResult = new JSONObject(result).getJSONObject(
                                        "returnData").toString();
                                responseCallback.onSucceed(finalResult);
                            } catch (JSONException ignore) {
                                responseCallback.onFailed("读取数据错误");
                            }
                        }
                    });
                } else {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            responseCallback.onFailed(finalMsg);
                        }
                    });
                }
            }
        });
    }

    public static void postJsonEnqueueWithoutHeader(String url, Map<String, String> params,
                                       final NetCallBack responseCallback, Object tag) {
        try {
            url = UrlConstants.ROOTURL + url;
            postJsonEnqueueWithHeader(url, params, responseCallback, tag, false);
        } catch (Exception e) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    responseCallback.onFailed("网络错误");
                }
            });
        }
    }

    public static void postJsonEnqueue(String url, Map<String, String> params,
                                       final NetCallBack responseCallback, Object tag) {
        try {
            url = UrlConstants.ROOTURL + url;
            postJsonEnqueueWithHeader(url, params, responseCallback, tag, true);
        } catch (Exception e) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    responseCallback.onFailed("网络错误");
                }
            });
        }
    }

    public static void postJsonEnqueueWithoutBaseUrl(String url, Map<String, String> params,
                                       final NetCallBack responseCallback, Object tag) {
        try {
            postJsonEnqueueWithHeader(url, params, responseCallback, tag, true);
        } catch (Exception e) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    responseCallback.onFailed("网络错误");
                }
            });
        }
    }

    private static void postJsonEnqueueWithHeader(String url, Map<String, String> params,
                                                  final NetCallBack responseCallback, Object tag,
                                                  boolean needHeader) throws Exception {
        if (isNetworkNotConnected(Wsdk.getInstance())) {
            onNoNetwork(responseCallback);
            return;
        }

        String bodyString = "\n";
        /*
          将map中的数取出，添加到JsonObject中去
         */
        JSONObject object = new JSONObject();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            object.put(entry.getKey(), entry.getValue());
        }
        bodyString = bodyString + object.toString();

        Request.Builder requestBuilder = new Request.Builder();
        if (needHeader) {
            if (!TextUtils.isEmpty(session)) {
                requestBuilder.addHeader("Cookie", session);
            }
            if (!TextUtils.isEmpty(SPUtil.getString(SPConstants.EQUIPSN))) {
                requestBuilder.addHeader("equipSN", SPUtil.getString(SPConstants.EQUIPSN));
            }
            if (!TextUtils.isEmpty(SPUtil.getString(SPConstants.TOKEN))) {
                requestBuilder.addHeader("Authorization", SPUtil.getString(SPConstants.TOKEN));
            }
            requestBuilder.addHeader("ClientType", "mobileApp");
        }
        Request request = requestBuilder.url(url).tag(tag).post(
                RequestBody.create(JSON, object.toString())).build();

        StringBuilder headerString = new StringBuilder("\n");
        Headers headers = request.headers();
        /*
          拿到Header中的 name和values
         */
        for (int i = 0; i < headers.size(); i++) {
            if (TextUtils.equals("Authorization", headers.name(i))) {
                continue;
            }
            headerString.append(headers.name(i)).append(":").append(headers.value(i)).append("\n");
        }

        LogUtil.d("POST_URL - " + url + "\nBODY - " + bodyString + "\nHEADER - " + headerString.toString());
        final String endUrl = url;
        /*
          进行OkHttp请求
         */
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                LogUtil.d("POST_ERROR - " + endUrl + "\n" + e.getMessage());

                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        responseCallback.onFailed("网络超时");
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                parseResponse(response, responseCallback, endUrl);
            }
        });
    }


    /**
     * 开启异步线程访问网络
     */
    public static void postEnqueueUploadAvatarByFile(String url, String filePath,
                                                     final NetCallBack responseCallback) {
        if (isNetworkNotConnected(Wsdk.getInstance())) {
            onNoNetwork(responseCallback);
            return;
        }
        url = UrlConstants.ROOTURL + url;
        Request request = new Request.Builder().url(url).header("Content-Type", "image/jpeg").post(
                RequestBody.create(MEDIA_TYPE_JPEG, new File(filePath))).build();
        final String endUrl = url;
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                LogUtil.d("POST_ERROR - " + endUrl + "\n" + e.getMessage());
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        responseCallback.onFailed("网络超时");
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                parseResponse(response, responseCallback, endUrl);
            }
        });
    }

    /**
     * 开启异步线程访问网络
     */
    public static void postEnqueueUploadAvatarByBody(String url, String text,
                                                     final NetCallBack responseCallback) {
        if (isNetworkNotConnected(Wsdk.getInstance())) {
            onNoNetwork(responseCallback);
            return;
        }
        url = UrlConstants.ROOTURL + url;
        Request request = new Request.Builder().url(url).post(
                RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), text)).build();
        final String endUrl = url;
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                LogUtil.d("POST_ERROR - " + endUrl + "\n" + e.getMessage());
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        responseCallback.onFailed("网络超时");
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                parseResponse(response, responseCallback, endUrl);
            }
        });
    }

    /**
     * 开启异步线程访问网络
     */
    public static void postEnqueueUploadAvatarByJson(String url, String text,
                                                     final NetCallBack responseCallback) {
        if (isNetworkNotConnected(Wsdk.getInstance())) {
            onNoNetwork(responseCallback);
            return;
        }
        url = UrlConstants.ROOTURL + url;
        JSONObject object = new JSONObject();
        try {
            object.put("data", text);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        Request request = new Request.Builder().url(url).post(
                RequestBody.create(JSON, object.toString())).build();
        final String endUrl = url;
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                LogUtil.d("POST_ERROR - " + endUrl + "\n" + e.getMessage());
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        responseCallback.onFailed("网络超时");
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                parseResponse(response, responseCallback, endUrl);
            }
        });
    }

    public static void putJsonEnqueue(String url, Map<String, String> params,
                                      final NetCallBack responseCallback, Object tag) {
        try {
            url = UrlConstants.ROOTURL + url;
            putJsonEnqueueWithHeader(url, params, responseCallback, tag, false);
        } catch (Exception e) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    responseCallback.onFailed("网络错误");
                }
            });
        }
    }

    private static void putJsonEnqueueWithHeader(String url, Map<String, String> params,
                                                 final NetCallBack responseCallback, Object tag,
                                                 boolean needToken) throws Exception {
        if (isNetworkNotConnected(Wsdk.getInstance())) {
            onNoNetwork(responseCallback);
            return;
        }
        StringBuilder bodyString = new StringBuilder("\n");
        JSONObject object = new JSONObject();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            object.put(entry.getKey(), entry.getValue());
            bodyString.append(entry.getKey()).append(":").append(entry.getValue()).append("\n");
        }
        LogUtil.d("要发送Cookie：" + session);

        Request.Builder builder = new Request.Builder().url(url);
        if (!TextUtils.isEmpty(SPUtil.getString(SPConstants.EQUIPSN))) {
            builder.addHeader("equipSN", SPUtil.getString(SPConstants.EQUIPSN));
        }
        if (!TextUtils.isEmpty(SPUtil.getString(SPConstants.TOKEN))) {
            builder.addHeader("Authorization", SPUtil.getString(SPConstants.TOKEN));
        }
        builder.addHeader("ClientType", "mobileApp");
        Request request = builder.tag(tag).put(RequestBody.create(JSON, object.toString())).build();

        LogUtil.d("PUT_URL - " + url + "\nBODY - " + bodyString.toString());
        final String endUrl = url;
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                LogUtil.d("PUT_ERROR - " + endUrl + "\n" + e.getMessage());

                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        responseCallback.onFailed("网络超时");
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                parseResponse(response, responseCallback, endUrl);
            }
        });
    }

    public static void deleteJsonEnqueue(String url, Map<String, String> params,
                                         final NetCallBack responseCallback, Object tag) {
        try {
            url = UrlConstants.ROOTURL + url;
            deleteJsonEnqueueWithHeader(url, params, responseCallback, tag, false);
        } catch (Exception e) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    responseCallback.onFailed("网络错误");
                }
            });
        }
    }

    private static void deleteJsonEnqueueWithHeader(String url, Map<String, String> params,
                                                    final NetCallBack responseCallback, Object tag,
                                                    boolean needToken) throws Exception {
        if (isNetworkNotConnected(Wsdk.getInstance())) {
            onNoNetwork(responseCallback);
            return;
        }
        StringBuilder bodyString = new StringBuilder("\n");
        JSONObject object = new JSONObject();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            object.put(entry.getKey(), entry.getValue());
            bodyString.append(entry.getKey()).append(":").append(entry.getValue()).append("\n");
        }
        LogUtil.d("要发送Cookie：" + session);

        Request.Builder builder = new Request.Builder().url(url);
        if (!TextUtils.isEmpty(SPUtil.getString(SPConstants.EQUIPSN))) {
            builder.addHeader("equipSN", SPUtil.getString(SPConstants.EQUIPSN));
        }
        if (!TextUtils.isEmpty(SPUtil.getString(SPConstants.TOKEN))) {
            builder.addHeader("Authorization", SPUtil.getString(SPConstants.TOKEN));
        }
        builder.addHeader("ClientType", "mobileApp");
        Request request = builder.tag(tag).delete(RequestBody.create(JSON, object.toString())).build();

        LogUtil.d("DELETE_URL - " + url + "\nBODY - " + bodyString.toString());
        final String endUrl = url;
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                LogUtil.d("DELETE_ERROR - " + endUrl + "\n" + e.getMessage());

                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        responseCallback.onFailed("网络超时");
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                parseResponse(response, responseCallback, endUrl);
            }
        });
    }

    public static void cancelCallWithTag(Object tag) {
        for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
        for (Call call : mOkHttpClient.dispatcher().runningCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
    }

    private static void parseResponse(Response response, final NetCallBack responseCallback,
                                      String endUrl) throws IOException {
        final String result = response.body().string();
        LogUtil.d("PARSE_RETURN - " + endUrl + "\n" + result);
        String returnCode = "404";
        String returnMsg = "网络超时";
        try {
            returnCode = new JSONObject(result).getString("returnCode");
            returnMsg = new JSONObject(result).getString("returnMsg");
        } catch (Exception e) {
            e.printStackTrace();
        }
        final String finalMsg = returnMsg;
        if ("200".equals(returnCode)) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        String finalResult = new JSONObject(result).getJSONObject("returnData")
                                .toString();
                        responseCallback.onSucceed(finalResult);
                    } catch (JSONException ignore) {
                        responseCallback.onFailed("读取数据错误");
                    }
                }
            });
        } else {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    responseCallback.onFailed(finalMsg);
                }
            });
        }
    }

    protected static void onNoNetwork(NetCallBack callBack) {
        if (callBack instanceof NoNetCallBack) {
            ((NoNetCallBack) callBack).onNoNetwork();
        } else {
            callBack.onFailed("网络未连接，请检查网络设置");
        }
    }

    /**
     * 检查当前是否有网络
     */
    protected static boolean isNetworkNotConnected(Context context) {
        if (context != null) {
            ConnectivityManager mConnectivityManager = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
            if (mNetworkInfo != null) {
                return !mNetworkInfo.isAvailable();
            }
        }
        return true;
    }

    public interface NetProgressCallback {
        void progress(float process);
    }

    public interface NetCallBack {
        void onSucceed(String json);

        void onFailed(String reason);
    }

    public interface NoNetCallBack
            extends NetCallBack {
        void onNoNetwork();
    }
}
